package com.tensquare.article.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tensquare.article.client.NoticeClient;
import com.tensquare.article.mapper.TbArticleMapper;
import com.tensquare.article.pojo.TbArticle;
import com.tensquare.article.pojo.TbNotice;
import com.tensquare.article.service.ITbArticleService;
import com.tensquare.entity.Result;
import com.tensquare.utils.IdWorker;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * <p>
 * 文章 服务实现类
 * </p>
 *
 * @author XXX
 * @since 2022-11-06
 */
@Service
public class TbArticleServiceImpl extends ServiceImpl<TbArticleMapper, TbArticle> implements ITbArticleService {

    @Autowired
    private TbArticleMapper tbArticleMapper;
    @Autowired
    private NoticeClient noticeClient;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;


    /**
     * 文章点赞
     * @param articleId
     * @param userId
     */
    @Override
    public void thumbup(String articleId, String userId) {
        //文章点赞
        TbArticle article = tbArticleMapper.selectById(articleId);
        article.setThumbup(article.getThumbup()+1);
        tbArticleMapper.updateById(article);
        
        //点赞成功后,在需要发送消息给文章作者(点对点消息)
        TbNotice tbNotice = new TbNotice();
        //接受消息用户的ID
        tbNotice.setReceiverid(article.getUserid());
        //进行操作用户的ID
        tbNotice.setOperatorid(userId);
        //操作类型(评论、点赞等)
        tbNotice.setAction("publish");
        //被操作对象,例如文章,评论等
        tbNotice.setTargettype("article");
        //被操作对象的Id,例如文章的Id,评论的Id
        tbNotice.setTargetid(articleId);
        //通知类型:sys系统消息;user用户信息
        tbNotice.setType("user");
        
        //保存消息
        noticeClient.save(tbNotice);

        //1、创建Rabbitmq管理器
        RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitTemplate.getConnectionFactory());

        
        //2、创建队列，每个作者都有自己的队列，通过用户id进行区分
        Queue queue = new Queue("article_thumbup_" + article.getUserid(), true);
        rabbitAdmin.declareQueue(queue);
        
        //3、发消息到队列中 
        rabbitTemplate.convertAndSend("article_thumbup_"+article.getUserid(),articleId);
        
    }

    /**
     * 根据文章id和用户ID，建立订阅的关系，保存的是文章作者id和用户id的关系
     * @param articleId
     * @param userId
     * @return
     */
    @Override
    public Boolean subscribe(String articleId, String userId) {
        
        //根据文章id查询文章作者id
        String authorId = tbArticleMapper.selectById(articleId).getUserid();
        
        //1、创建Rabbitmq管理器
        RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitTemplate.getConnectionFactory());
        
        //2、声明Direct类型交换机，处理新增文章消息
        DirectExchange exchange=new DirectExchange("article_subscribe");
        
            rabbitAdmin.declareExchange(exchange);
       
        
        //3、创建队列，每个用户都有自己的队列，通过用户id进行区分
        Queue queue = new Queue("article_subscribe_" + userId, true);
        

        //4、声明交换机和队列的绑定关系，需要确保队列只收到对应的作者新增文章消息
        // 通过路由键进行绑定者，队列只收到绑定作者的文章消息
        Binding binding = BindingBuilder.bind(queue).to(exchange).with(authorId);
        

        //存放用户订阅信息的集合key,里面存放作者id
        String userKey="article_subscribe_"+userId;
        //存放作者订阅者信息的集合key,里面存放订阅者id
        String authorKey="article_author_"+authorId;
       
        //查询用户的订阅关系，是否有订阅作者  用set集合唯一
        Boolean flag = redisTemplate.boundSetOps(userKey).isMember(authorId);
        
        
        if (flag==true){
            //如果订阅作者，就取消订阅
            //在用户订阅信息的集合中，删除作者
            redisTemplate.boundSetOps(userKey).remove(authorId);
            //作者订阅者信息的集合中，删除订阅者
            redisTemplate.boundSetOps(authorKey).remove(userId);
            
            //若取消订阅，删除队列绑定关系
            rabbitAdmin.removeBinding(binding);
            
            
            //返回false
            return false;
       
        }else {
            //如果没有订阅作者，就进行订阅
            //在用户订阅信息中，添加订阅的作者
            redisTemplate.boundSetOps(userKey).add(authorId);
            //在作者订阅者信息中，添加订阅者
            redisTemplate.boundSetOps(authorKey).add(userId);
            
            //若订阅成功,声明要绑定的队列
            rabbitAdmin.declareQueue(queue);
            //添加绑定关系 路由键关系
            rabbitAdmin.declareBinding(binding);
            
            //返回true
            return true;
        }
        

        
    }

    /**
     * 根据文章条件来查询文章分页列表
     * @param map
     * @param page
     * @param size
     * @return
     */
    @Override
    public Page<TbArticle> findByPage(Map<String, Object> map, Integer page, Integer size) {
        //设置查询条件
        QueryWrapper<TbArticle> wrapper = new QueryWrapper<>();
        Set<String> keySet = map.keySet();
        for (String key: keySet) {
            //第一种方法
            //if(map.get(key)!=null){
            //    wrapper.eq(key,map.get(key));
            //}
            //第二种方法
            wrapper.eq(map.get(key)!=null,key,map.get(key));
        }
        //设置分页
        Page<TbArticle>  pageDate= new Page<>(page, size);

        //执行查询
        Page<TbArticle> articlePage = tbArticleMapper.selectPage(pageDate, wrapper);
        //pageDate.setRecords(list);
        //返回数据

        return articlePage;
    }

    /**
     * 根据文章id删除文章
     * @param articleId
     * @return
     */
    @Override
    public int deleteById(String articleId) {
        return tbArticleMapper.deleteById(articleId);
    }

    /**
     * 根据文章Id修改文章
     * @param article
     * @return
     */
    @Override
    public boolean updateArticleById(TbArticle article) {
        //根据主键修改
        //设置默认条件
        //article.setUpdatetime(LocalDateTime.now());
        //Integer integer = tbArticleMapper.updateById(article);
        //根据条件修改
        //创建条件对象
        // 优化
        if (article.getId()!=null){
            QueryWrapper<TbArticle> wrapper = new QueryWrapper<>();
            //设置条件
            wrapper.eq("id",article.getId());

            //设置默认条件
            article.setUpdatetime(LocalDateTime.now());

            Integer integer =tbArticleMapper.update(article,wrapper);
            return true;
        }
        return false;
    }

    /**
     * 新增文章
     * @param article
     * @return
     */
    @Override
    public int saveArticle(TbArticle article) {
        //TODO: 使用jwt鉴权获取当前用户的信息，用户Id,也就是文章作者Id
        String userId="2";
        article.setUserid(userId);
        
        
        
        //使用分布式id生成器
        String id = idWorker.nextId() + "";
        article.setId(id);
        //初始化
        //article.setVisits(0); //浏览量
        //article.setThumbup(0); //点赞数
        //article.setComment(0); //评论数
        article.setCreatetime(LocalDateTime.now());
        int insert = tbArticleMapper.insert(article);
        
        //新增文章后，创建消息，通知订阅者
        
        //获取订阅者后信息
        //获取存放作者订阅者信息的集合key,里面存放订阅者id
        String authorKey="article_author_"+userId;
        Set<String> set = redisTemplate.boundSetOps(authorKey).members();
        System.out.println("set="+set);
        
        //最好添加一个条件判断
        //if (set.size()>0){

            TbNotice tbNotice =null;
            //给订阅者创建消息通知
            for (String uid : set) {
                //创建消息对象
                tbNotice = new TbNotice();
                //接收消息用户的Id
                tbNotice.setReceiverid(uid);
                //进行操作用户的Id
                tbNotice.setOperatorid(userId);
                //操作类型(评论，点赞等)
                tbNotice.setAction("publish1");
                //被操作对象,例如文章,评论等
                tbNotice.setTargettype("acticle1");
                //被操作对象的Id,例如文章的Id,评论的Id
                tbNotice.setTargetid(id);
                //通知类型
                tbNotice.setType("sys");
                Result save = noticeClient.save(tbNotice);
                
                
            }
        //发消息给RabbitMQ,就是新消息的通知
        //第一个参数是交换机名，使用之前完成的订阅功能的交换机
        //第二个参数是路由键，使用时文章作者的id作为路由键
        //第三个参数是消息内容，这里只是完成新消息提醒，内容是文章Id
        rabbitTemplate.convertAndSend("article_subscribe", userId, id);
            
        //}
        

        

        return insert;
    }

    /**
     *
     * @param articleId
     * @return
     */
    @Override
    public TbArticle findById(String articleId) {
        return tbArticleMapper.selectById(articleId);
    }

    /**
     * 查询文章全部列表
     * @return
     */
    @Override
    public List<TbArticle> findAll() {
        return tbArticleMapper.selectList(null);
    }
}
